#!/usr/bin/python
import os
import shutil
import tempfile
import difflib

from uya.routerconf.configmod import config as configmod
from uya.routerconf.configmod import generic
from uya import routerconf
from uya.routerconf.configmod.configobj import validate
from uya.routerconf import validators

def init_filesystems(config_dir):
    """Load configuration and set routerconf.filename paths for testing.""" 
    config_file = os.path.join(config_dir, "uya.conf")
    template_file = os.path.join(config_dir, "uya.template.conf")
    validator = validate.Validator(validators.get_validators())
    config = configmod.load_config(config_file, template_file, validator)
                
    original_dir = os.path.join(config_dir, "filesystems/original")
    temp_dir = os.path.join(config_dir, "filesystems/temp")
    if os.path.isdir(temp_dir):
        shutil.rmtree(temp_dir)
    expected_dir = os.path.join(config_dir, "filesystems/expected")
    shutil.copytree(original_dir, temp_dir)
    for key, filename in routerconf.default_filenames.iteritems():
        temp_filename = os.path.join(temp_dir, filename.lstrip("/"))
        routerconf.filenames[key] = temp_filename
    return config

def init_filesystems_for_get(tests_dir):
    """Load configuration and set routerconf.filename paths for testing."""
    original_dir = os.path.join(tests_dir, "fixtures/filesystems/original") 
    config_file = os.path.join(tests_dir, "fixtures/uya.conf")
    template_file = os.path.join(tests_dir, "fixtures/uya.template.conf")
    validator = validate.Validator(validators.get_validators())
    config = configmod.load_config(config_file, template_file, validator)
    for key, filename in routerconf.default_filenames.iteritems():
        original_filename = os.path.join(original_dir, filename.lstrip("/"))
        routerconf.filenames[key] = original_filename
    return config

def get_files(config_dir, check_files):
    """Compare two filesystems for testing."""        
    temp_dir = os.path.join(config_dir, "filesystems/temp")
    expected_dir = os.path.join(config_dir, "filesystems/expected")
    for filename in check_files:
        expected_filename = os.path.join(expected_dir, filename.lstrip("/"))
        temp_filename = os.path.join(temp_dir, filename.lstrip("/"))
        yield filename, expected_filename, temp_filename
    
class BaseTestConfig:
    """Base class to be inherit by other TestCases. Helps on comparing
    filesystem contents (original vs expected)."""

    def create_temporal(self, data=None):
        """Create a temporal file path."""
        fd, path = tempfile.mkstemp()
        if data:
            os.write(fd, data)
        os.close(fd)
        return path

    def assertEqualSeqs(self, expected, value):
        self.assertEqual(expected, value, 
            "\n".join(difflib.context_diff(value, expected)))
        
    def _compare_filesystems(self, npass, retval, expected_modified_filenames,
            expected_commands):
        """Compare a generated filesystem with the expected one. If error,
        show differences."""
        config_dir = os.path.join(self.root_tests_dir, "fixtures")
        if retval and npass == 0:
            modified_files, commands = retval
            temp_dir = os.path.join(config_dir, "filesystems/temp")
            modified_files2 = [fn[len(temp_dir):] for fn in modified_files]
            self.assertEqual(list(sorted(expected_modified_filenames)), 
                list(sorted(modified_files2)))
            self.assertEqual(expected_commands, map(list, commands))
        
        info = get_files(config_dir, expected_modified_filenames)
        for filename, expected_filename, temp_filename in info:
            expected = generic.readlines(expected_filename)
            temp = generic.readlines(temp_filename)
            self.assertEqual(expected, temp, 
                "Error on file '%s' (pass %d). Diff:\n%s" % 
                (temp_filename, npass, "".join(difflib.context_diff(temp, expected))))
        
    def compare_filesystems(self, expected_modified_filenames, 
            expected_commands, func):
        """Compare two filesystems. Changes should be idempotent."""
        config_dir = os.path.join(self.root_tests_dir, "fixtures")
        config = init_filesystems(config_dir)
        #for npass in (0, 1):
        for npass in (0,):
            retval = func(config)
            self._compare_filesystems(npass, retval, 
                expected_modified_filenames, expected_commands)
        temp_dir = os.path.join(config_dir, "filesystems/temp")
        shutil.rmtree(temp_dir)
